package me.zhengjie.common.utils.cypto

import cn.hutool.crypto.CryptoException
import me.zhengjie.logging.slf4j.*
import java.security.NoSuchAlgorithmException
import java.util.*
import javax.crypto.Cipher
import javax.crypto.KeyGenerator

import javax.crypto.SecretKey
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec

import java.security.SecureRandom


/**
 * @program: pilecloud
 * @description: AESUtils 工具类
 * @author: Mr.Xiao
 * @create: 2018-07-23 17:41
 */
object AESUtils {

    const val KEY_ALGORITHM = "AES"
    const val KEY_ALGORITHM_PADDING = "AES/CBC/PKCS5Padding"
    @Throws(Exception::class)
    fun getSecretKey(seed: ByteArray?): SecretKey {
        val secureRandom = SecureRandom(seed)
        val keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM)
        keyGenerator.init(secureRandom)
        return keyGenerator.generateKey()
    }

    fun encrypt(data: ByteArray?, encryptKey: ByteArray?, iv: ByteArray?): ByteArray {
        val zeroIv = IvParameterSpec(iv)
        val key = SecretKeySpec(encryptKey, KEY_ALGORITHM)
        return encrypt(data, zeroIv, key)
    }

    fun decrypt(data: ByteArray?, decryptKey: ByteArray?, iv: ByteArray?): ByteArray {
        val zeroIv = IvParameterSpec(iv)
        val key = SecretKeySpec(decryptKey, KEY_ALGORITHM)
        return decrypt(data, zeroIv, key)
    }

    fun encrypt(data: ByteArray?, zeroIv: IvParameterSpec, keySpec: SecretKeySpec): ByteArray {
        return try {
            //Profiler.enter("time cost on [aes encrypt]: data length=" + data.length);
            val cipher = Cipher.getInstance(KEY_ALGORITHM_PADDING)
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, zeroIv)
            cipher.doFinal(data)
        } catch (e: Exception) {
            logError(
                "AES encrypt ex, iv={" + Arrays.toString(zeroIv.iv) + "}, key={" + Arrays.toString(keySpec.encoded) + "}"            )
            throw CryptoException("AES encrypt ex", e)
        }
    }

    fun decrypt(data: ByteArray?, zeroIv: IvParameterSpec, keySpec: SecretKeySpec): ByteArray {
        return try {
            //Profiler.enter("time cost on [aes decrypt]: data length=" + data.length);
            val cipher = Cipher.getInstance(KEY_ALGORITHM_PADDING)
            cipher.init(Cipher.DECRYPT_MODE, keySpec, zeroIv)
            cipher.doFinal(data)
        } catch (e: Exception) {
            logError(
                "AES encrypt ex, iv={" + Arrays.toString(zeroIv.iv) + "}, key={" + Arrays.toString(keySpec.encoded) + "}"
            )
            throw CryptoException("AES decrypt ex", e)
        }
    }

    /**
     * @Description: 随机生成秘钥
     * @Param: []
     * @return: java.lang.String
     * @Author: Mr.Xiao
     * @Date: 2018/8/9
     */
    fun generateAESKey(): String? {
        try {
            val kg = KeyGenerator.getInstance(KEY_ALGORITHM)
            kg.init(128) //要生成多少位，只需要修改这里即可128, 192或256
            val sk = kg.generateKey()
            val b = sk.encoded
            return byteToHexString(b)
        } catch (e: NoSuchAlgorithmException) {
            e.printStackTrace()
            logError(e.message!!)
        }
        return null
    }

    /**
     * @Description: 随机生成秘钥
     * @Param: []
     * @return: java.lang.String
     * @Author: Mr.Xiao
     * @Date: 2018/8/9
     */
    fun generateAESKey(key: String): String? {
        try {
            val kg = KeyGenerator.getInstance(KEY_ALGORITHM)
            // kg.init(128);//要生成多少位，只需要修改这里即可128, 192或256
            //SecureRandom是生成安全随机数序列，password.getBytes()是种子，只要种子相同，序列就一样，所以生成的秘钥就一样。
            kg.init(128, SecureRandom(key.toByteArray()))
            val sk = kg.generateKey()
            val b = sk.encoded
            val s = byteToHexString(b)
            println(s)
            println("十六进制密钥长度为" + s.length)
            println("二进制密钥的长度为" + s.length * 4)
        } catch (e: NoSuchAlgorithmException) {
            e.printStackTrace()
            logError(e.message!!)
        }
        return null
    }

    fun byteToHexString(bytes: ByteArray): String {
        val sb = StringBuffer()
        for (i in bytes.indices) {
            val strHex = Integer.toHexString(bytes[i].toInt())
            if (strHex.length > 3) {
                sb.append(strHex.substring(6))
            } else {
                if (strHex.length < 2) {
                    sb.append("0$strHex")
                } else {
                    sb.append(strHex)
                }
            }
        }
        return sb.toString()
    }
}